#!/usr/bin/python3
# Copyright (c) 2009-2018 Canonical Ltd
#
# AUTHOR:
# Michael Vogt <mvo@ubuntu.com>
# Balint Reczey <rbalint@ubuntu.com>
#
# unattended-upgrade-shutdown - helper that checks if a
# unattended-upgrade is in progress and waits until it exists
#
# This file is part of unattended-upgrades
#
# unattended-upgrades is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# unattended-upgrades is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with unattended-upgrades; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

import copy
import dbus
import signal
import sys
import time
import datetime
import logging
import logging.handlers
import gettext
import subprocess
import os.path
import os
import configparser
import psutil
from pytz import timezone
# for dbus signal handling
try:
    from dbus.mainloop.glib import DBusGMainLoop
    from gi.repository import GLib
except ImportError:
    pass

from optparse import OptionParser, Values
Values  # pyflakes
from gettext import gettext as _
from threading import Event
from enum import IntEnum, Enum
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.schedulers.background import BackgroundScheduler
import random
import threading
import re

try:
    import apt_pkg
except Exception:
    # if there is no python-apt no unattended-upgrades can run so not
    # need to stop the shutdown
    logging.exception("importing of apt_pkg failed, exiting")
    sys.exit(0)

# progress information is written here
PROGRESS_LOG = "/var/run/unattended-upgrades.progress"
PID_FILE = "/var/run/unattended-upgrades.pid"
LOCK_FILE = "/var/run/kylin-unattended-upgrades.lock"
PKGS_TO_INSTALL_FLAG_FILE="/var/lib/unattended-upgrades/OTA_PKGS_TO_INSTALL"
TIME_STAMP = "/var/lib/unattended-upgrades/unattended-upgrades-timestamp"
OTA_PKGS_TO_INSTALL_LIST="/var/lib/unattended-upgrades/ota_pkgs_to_install_list"

## analytic unattended-upgrades-policy.conf start
POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY = "autoUpgradePolicy"
AUTO_UPGRADE_POLICY_OPTION_PREDOWNLOAD = "preDownload"
AUTO_UPGRADE_POLICY_OPTION_AUTOUPGRADE = "autoUpgradeState"
AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_MODE = "downloadMode"
AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_TIME = "downloadTime"
AUTO_UPGRADE_POLICY_OPTION_INSTALL_MODE = "installMode"
AUTO_UPGRADE_POLICY_OPTION_UPGRADE_INTERVAL = "upgradeInverval"
INTERVAL_DOWN_INSTALL = 120   # 下载安装的间隔 分钟
INSTALL_RANDOM = 5            # 安装时间随机数范围0-INSTALL_RANDOM  分钟
DOWNLOAD_RANDOM = 180         # 下载时间随机数范围0-DOWNLOAD_RANDOM 分钟
PREDOWNLOAD_RANDOM = 180

class FeatureSwitch(Enum):
    ON = 'on'
    OFF = 'off'

class DownloadMode(Enum):      # 下载模式
    TIMING_DOWNLOAD = 'timing' # 定时下载
    MANUAL_DOWNLOAD = 'manual' # 手动下载

class InstallMode(Enum):       # 安装模式
    TIMING_INSTALL = 'timing'  # 定时安装
    MANUAL_INSTALL = 'manual'  # 手动安装
    BEFORE_SHUTDOWN_INSTALL = 'bshutdown'  # 关机前安装

class TimeElement(IntEnum):
    TIME_HOUR = 0
    TIME_MINUTE = 1
    TIME_NUM = 2

## analytic unattended-upgrades-policy.conf end

UNATTENDED_UPGRADE_CONFIG_FILE_PATH="/var/lib/unattended-upgrades/unattended-upgrade.conf"
UNATTENDED_UPGRADE_POLICY_FILE_PATH="/var/lib/unattended-upgrades/unattended-upgrades-policy.conf"
NOTIFICATION_PIPE = '/tmp/notification.pipe'
UNATTENDED_UPGRADE_TIMESTAMP = "/var/lib/unattended-upgrades/unattended-upgrades-timestamp"

flag_file_list = ["/var/lib/unattended-upgrades/OTA_PKGS_TO_INSTALL",\
    "/var/lib/kylin-auto-upgrade/kylin-autoupgrade.conf","/tmp/notify.pid"]

def reload_options_config():
    #添加默认保留旧配置
    apt_pkg.config["DPkg::Options::"] = "--force-confold"
    options_new = list(set(apt_pkg.config.value_list("DPkg::Options")))
    for option in ("--force-confnew","--force-confdef"):
        if option in options_new:
            options_new.remove(option)
    #清除所有配置重新加载
    apt_pkg.config.clear("DPkg::Options")
    for option in options_new:
        apt_pkg.config["DPkg::Options::"] = option
    #去除安装推荐和建议的软件包
    if apt_pkg.config.find_b("APT::Install-Recommends",False) == True:
        apt_pkg.config.clear("APT::Install-Recommends")
    if apt_pkg.config.find_b("APT::Install-Suggests",False) == True:
        apt_pkg.config.clear("APT::Install-Suggests")
    if apt_pkg.config.find("Dir::Etc::sourceparts","")!="":
        apt_pkg.config["Dir::Etc::sourceparts"]=""
    apt_pkg.init_system()

def get_mavis_capacity():
    batterycapacity = 100
    try:
        with open("/sys/class/power_supply/BAT1/capacity", 'r') as f:
            batterycapacity = int(f.readline())
    except:
        logging.error("no battery device")
    return batterycapacity
        

def is_dpkg_journal_dirty():
    # type: () -> bool
    """
    Return True if the dpkg journal is dirty
    (similar to debSystem::CheckUpdates)
    """
    logging.debug("checking whether dpkg journal is dirty")
    d = os.path.join("/var/lib/dpkg/",
        #os.path.dirname(apt_pkg.config.find_file("Dir::State::status")),
        "updates")
    for f in os.listdir(d):
        if re.match("[0-9]+", f) or re.match("tmp.i",f):    
            return True
    return False

def get_abnormally_installed_pkg_count():
    output = subprocess.check_output('dpkg -l|grep ^i[^i]|wc -l',shell=True)
    return output.decode().strip()


def GetDateTime():
    return datetime.datetime.now().replace(microsecond=0)

def ReadOsRelease(file):
    osreleasedict = {}
    try:
        with open(file) as f:
            lines = f.readlines()
            for line in lines:
                ls = line.strip().split('=',1)
                osreleasedict.update({ls[0]:ls[1].strip('"')})
    except Exception as e:
        pass
    if 'SUB_PROJECT_CODENAME' not in osreleasedict.keys():
        osreleasedict.update({'SUB_PROJECT_CODENAME':''})
    return osreleasedict

def FindRuningUnattendedUpgrades():
    if os.path.exists(PID_FILE):
        pid = open(PID_FILE).readline().strip()
        logging.info("runing unattended-upgrades pid:%s"%pid)
        try:
            ps = psutil.Process(int(pid))
            logging.debug("process name:%s,process status:%s"%(ps.name(),ps.status()))
            return ps.is_running()
        except Exception as e:
            logging.error(e)
    return False

def ReadValueFromFile(file,section,option):
    config=configparser.ConfigParser(allow_no_value=True)
    try:
        config.read(file)
        value = config[section][option]
    except Exception as e:
        return ''
    return value

def WriteValueToFile(file,section,option,value):
    config=configparser.ConfigParser(allow_no_value=True)
    config.add_section(section)
    config.set(section,option,value)
    config.write(open(file,"w"))

def clean_flag_files(filelist):
    WriteValueToFile(UNATTENDED_UPGRADE_CONFIG_FILE_PATH,"UNATTENDED_UPGRADE","autoupdate_run_status","idle")
    for file in filelist:
        if os.path.exists(file):
            os.remove(file)
def init():
    if not os.path.exists(NOTIFICATION_PIPE):
        os.mkfifo(NOTIFICATION_PIPE)
        
def get_random_time(stime,random_range):
    now = datetime.datetime.now()
    delta = random.randint(0,random_range)
    actual_time = now + datetime.timedelta(minutes=delta)
    try:
        start_time = datetime.datetime.strptime(stime,"%H:%M")
        start=datetime.datetime(now.year,now.month,now.day,start_time.hour,start_time.minute,0,0)
        actual_time = start+datetime.timedelta(minutes=delta)    
    except Exception as e:
        logging.error(e)
    return actual_time
        
def task(task):
    env = copy.copy(os.environ)
    cmd = "date"
    if task in ["predownload","download"]:
        cmd = "kylin-unattended-upgrade --download-only"
    elif task == "install":
        cmd = "kylin-unattended-upgrade --install-only --mode=timing"
    elif task == "download_and_install":
        cmd = "kylin-unattended-upgrade --download-only&&kylin-unattended-upgrade --install-only --mode=timing" 
        #do not check updgrade period when download and install
    else:
        pass
    ret = subprocess.run([cmd], shell=True,env=env)
    logging.debug("task:%s return code:%d"%(task,ret.returncode))
    return ret.returncode      
          
def background_scheduler_init(background_scheduler):
    
    background_scheduler.start()
    
    random_time = get_random_time(autoupgradepolicy.GetOptionValue('downloadTime'),DOWNLOAD_RANDOM)
    background_scheduler.add_job(task,'cron', args=['download'],id='download', \
                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
    
    random_time = random_time + datetime.timedelta(minutes=INTERVAL_DOWN_INSTALL)
    background_scheduler.add_job(task,'cron', args=['install'],id='install', \
                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
      
    random_time = get_random_time(autoupgradepolicy.GetOptionValue('preDownloadTime'),PREDOWNLOAD_RANDOM)
    background_scheduler.add_job(task,'cron', args=['predownload'],id='predownload', \
                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
    
    if autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on':
        if autoupgradepolicy.GetOptionValue('downloadMode') != 'timing':
            background_scheduler.pause_job('download')
        if autoupgradepolicy.GetOptionValue('installMode') != 'timing':
            background_scheduler.pause_job('install')
    else:
        background_scheduler.pause_job('download')
        background_scheduler.pause_job('install')
        
    if autoupgradepolicy.GetOptionValue('preDownload') != 'on':
        background_scheduler.pause_job('predownload')
        
    
    joblist = background_scheduler.get_jobs()
    
    for job in joblist:    
        logging.debug("job:%s,next run time:%s"%(job.id,job.next_run_time))
'''
def do_usplash(msg):
    # type: (str) -> None
    if os.path.exists("/sbin/usplash_write"):
        logging.debug("Running usplash_write")
        subprocess.call(["/sbin/usplash_write", "TEXT", msg])
        subprocess.call(["/sbin/usplash_write", "PULSATE"])
'''

def do_plymouth(msg):
    # type: (str) -> None
    if os.path.exists("/bin/plymouth"):
        for line in msg.split("\n"):
            logging.debug("Running plymouth --text")
            subprocess.call(["/bin/plymouth", "message", "--text", line])

def do_plymouth_splash():
    if os.path.exists("/bin/plymouth"):
        logging.debug("Running plymouth --splash")
        subprocess.run(["/sbin/plymouthd", "--mode=shutdown","--attach-to-session"])
        #subprocess.run(["/sbin/plymouthd", "--mode=update","--attach-to-session"])
        #subprocess.run(["/bin/plymouth","--update=kylin update"])
        subprocess.Popen(["/bin/plymouth", "show-splash","--wait"])
        subprocess.call(["/bin/plymouth","system-update","--progress=0"])

def log_msg(msg, level=logging.WARN):
    # type: (str, int) -> None
    """ helper that will print msg to usplash, plymouth, console """
    logging.log(level, msg)
    #do_plymouth(msg)
    #do_usplash(msg)


def log_progress():
    # type: () -> None
    """ helper to log the install progress (if any) """
    # wait a some seconds and try again
    '''
    msg = _("Unattended-upgrade in progress during shutdown, "
            "please don't turn off the computer")
    '''
    # progress info
    progress_file = PROGRESS_LOG
    if os.path.exists(progress_file):
        progress_text = open(progress_file).read()
        logging.debug("progress text content %s"%progress_text)
        if len(progress_text):
            progress = int(float(progress_text))
            subprocess.call(["/bin/plymouth","system-update","--progress=%d"%progress])
            msg = "upgrage progress %s"%progress_text
            log_msg(msg)
    
def signal_term_handler(signal,frame):
    # type: (int, object) -> None
    logging.warning("SIGTERM received, will stop")
    os._exit(1)

def signal_stop_unattended_upgrade():
    """ send SIGTERM to running unattended-upgrade if there is any """
    pidfile = PID_FILE#"/var/run/unattended-upgrades.pid"
    if os.path.exists(pidfile):
        pid = int(open(pidfile).read())
        logging.debug("found running unattended-upgrades pid %s" % pid)
        try:
            os.kill(pid, signal.SIGTERM)
        except ProcessLookupError:
            logging.debug("sending SIGTERM failed because unattended-upgrades "
                          "already stopped")

def exit_log_result(success):
    if os.path.exists(PKGS_TO_INSTALL_FLAG_FILE):
        os.remove(PKGS_TO_INSTALL_FLAG_FILE)
        subprocess.call(["/bin/plymouth","system-update","--progress=100"])
        time.sleep(3)
        subprocess.run(["/bin/plymouth","quit","--retain-splash"])
    if success:
        logging.debug("All upgrades installed")
        #log_msg(_("All upgrades installed"), logging.INFO)
        os._exit(0)
        #sys.exit(0)
    else:
        log_msg(_("Unattended-upgrades stopped. There may be upgrades"
                  " left to be installed in the next run."), logging.INFO)
        os._exit(1)
        #sys.exit(1)

# 检查时间安全性 -- minute > 59; hour > 23;
def check_time_safety(inTime):
    if inTime['m'] > 59 :
        inTime['h'] = inTime['h'] + inTime['m']//60
        inTime['m'] = inTime['m']%60

    if inTime['h'] > 23 :
        inTime['h'] = inTime['h'] - 24

    outTime = inTime
    return outTime

# 时间添加随机数
def convert_time_by_random(inTime, inRandom):
    diff = random.randint(0,inRandom)
    inTime['h']=inTime['h'] + diff // 60
    inTime['m']=inTime['m'] + diff % 60
    outTime = check_time_safety(inTime)
    return outTime

class TimerThread(threading.Thread):
    def __init__(self, scheduler):
        threading.Thread.__init__(self)
        self.scheduler = scheduler
    def run(self):
        self.scheduler.start()

class KylinSystemUpdater:
    def __init__(self) -> None:
        DBusGMainLoop(set_as_default=True)
        self.loop = GLib.MainLoop()
        self.system_bus = dbus.SystemBus()
        self.update_proxy = self.system_bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade')
        self.update_interface = dbus.Interface(self.update_proxy,dbus_interface='com.kylin.systemupgrade.interface')      

    def GetConfigValue(self,section,value):
        return self.update_interface.GetConfigValue(section,value)

    def SetConfigValue(self,section,option,value):
        return self.update_interface.SetConfigValue(section,option,value)
    
class AutoUpgradePolicy():
    def __init__(self) -> None:
        self.autoupgradepolicy = {}
        if os.path.exists(UNATTENDED_UPGRADE_POLICY_FILE_PATH):
            config=configparser.ConfigParser(allow_no_value=True)
            config.optionxform = str
            config.read(UNATTENDED_UPGRADE_POLICY_FILE_PATH)
            for option in config.options('autoUpgradePolicy'):
                self.autoupgradepolicy.update({option:config['autoUpgradePolicy'][option]})
        for key in self.autoupgradepolicy.keys():
            logging.debug("%s:%s"%(key,self.autoupgradepolicy[key]))        
       
    def SetOptionValue(self,option,value):
        self.autoupgradepolicy.update({option:value})
   
    def GetOptionValue(self,option):
        try:
            return self.autoupgradepolicy[option]
        except Exception:
            return ''
    
    def reload_config(self):
        if os.path.exists(UNATTENDED_UPGRADE_POLICY_FILE_PATH):
            config=configparser.ConfigParser(allow_no_value=True)
            config.optionxform = str
            config.read(UNATTENDED_UPGRADE_POLICY_FILE_PATH)
            for option in config.options('autoUpgradePolicy'):
                self.autoupgradepolicy.update({option:config['autoUpgradePolicy'][option]})
        for key in self.autoupgradepolicy.keys():
            logging.debug("%s:%s"%(key,self.autoupgradepolicy[key]))
    
class UnattendedUpgradesShutdown():
    # 加载配置文件 unattended-upgrades-policy.conf
    '''
    def loadcfg(self):
        if os.path.exists(UNATTENDED_UPGRADE_POLICY_FILE_PATH):
            self.preDownload = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_PREDOWNLOAD)
            self.autoUpgrade = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_AUTOUPGRADE)
            self.download_mode = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_MODE)
            self.install_mode = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_INSTALL_MODE)
            download_time = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_TIME)
            # self.download_random = int(kylin_system_updater.GetConfigValue('AutoUpgradeConfig','downloadRandom')[1])
            # self.upgrade_interval = int(kylin_system_updater.GetConfigValue('AutoUpgradeConfig','upgradeInterval')[1])
            # logging.info("download random:%s,upgrade interval:%s"%(self.download_random,self.upgrade_interval))
            # upgradeInterval = int(ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, 'upgradeInverval'))
            
            if os_release_info['PROJECT_CODENAME'] == 'V10SP1-edu' and os_release_info['SUB_PROJECT_CODENAME']=='mavis':
                self.download_time['h'] = 10
                self.download_time['m'] = 0
                self.download_time_r = convert_time_by_random(self.download_time, 5)
                logging.debug("upgrade time: [%d] [%d] predown[%s] autoupgrade[%s] d-mode[%s] i-mode[%s]", 
                self.download_time_r['h'], self.download_time_r['m'],self.preDownload, self.autoUpgrade, \
                    self.download_mode, self.install_mode)
                return
            
            timelist = download_time.strip().split(':')
            
            if len(timelist) != TimeElement.TIME_NUM:
                logging.debug("unattended-upgrades-policy.conf time err %s",download_time)
                return
            
            # 检查 传入时间 安全性
            try:
                tmphour = int(timelist[TimeElement.TIME_HOUR])
            except ValueError:
                logging.debug("unattended-upgrades-policy.conf download_time h error")
                return
            try:
                tmpminute = int(timelist[TimeElement.TIME_MINUTE])
            except ValueError:
                logging.debug("unattended-upgrades-policy.conf download_time m error")
                return

            self.download_time['h'] = tmphour
            self.download_time['m'] = tmpminute
            self.download_time_r = convert_time_by_random(self.download_time, self.download_random)
            self.install_time['h']  = self.download_time_r['h']
            self.install_time['m']  = self.download_time_r['m'] + INTERVAL_DOWN_INSTALL
            self.install_time_r = convert_time_by_random(self.install_time, INSTALL_RANDOM)
            logging.debug("upgrade time: [%d:%d] [%d:%d] predown[%s] autoupgrade[%s] d-mode[%s] i-mode[%s]", 
            self.download_time_r['h'], self.download_time_r['m'],self.install_time_r['h'],self.install_time_r['m'],
            self.preDownload, self.autoUpgrade, self.download_mode, self.install_mode)
        else:
            logging.debug("unattended-upgrades-policy.conf not exist")
        return
    '''
    def __init__(self, options):
        # type: (Values) -> None
        self.options = options
        self.max_delay = options.delay * 60
        self.mainloop = GLib.MainLoop()
        self.iter_timer_set = False
        self.apt_pkg_reinit_done = None
        self.shutdown_pending = False
        self.on_shutdown_mode = None
        self.on_shutdown_mode_uu_proc = None
        self.start_time = None
        self.lock_was_taken = False
        self.signal_sent = False
        self.stop_signal_received = Event()
        '''
        self.download_mode = DownloadMode.TIMING_DOWNLOAD.value #下载模式
        self.install_mode = InstallMode.TIMING_INSTALL.value #安装模式
        self.download_time = {'h':9, 'm':0}  #定时下载时间 09:00
        self.install_time = {'h':12, 'm':0}  #定时安装时间 12:00
        self.download_time_r = convert_time_by_random(self.download_time, DOWNLOAD_RANDOM) #随机化定时下载时间
        self.install_time_r = convert_time_by_random(self.install_time, INSTALL_RANDOM) #随机化定时安装时间
        self.preDownload = 'off' #预下载开关
        self.autoUpgrade = 'off' #自动更新开关
        self.download_job = None
        self.install_job = None
        self.startup_download_job = None
        self.scheduler = BlockingScheduler(timezone = "Asia/Shanghai")
        '''
        try:
            hasattr(GLib, "MainLoop")
            DBusGMainLoop(set_as_default=True)
        except NameError:
            logging.error("DBusGMainLoop error")
            pass
        
        try:
            self.inhibit_lock = self.get_inhibit_shutdown_lock()
        except dbus.exceptions.DBusException:
            logging.warning("Could not get delay inhibitor lock")
            self.inhibit_lock = None
        self.logind_proxy = None
        self.update_proxy = None
        self.wait_period = min(3, self.get_inhibit_max_delay() / 3)
        self.preparing_for_shutdown = False
        #self.loadcfg()

    def get_update_proxy(self):
        if not self.update_proxy:
            bus = dbus.SystemBus()
            self.update_proxy = bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade')
        return self.update_proxy

    def get_update_interface(self):
        self.update_interface = dbus.Interface(self.update_proxy,dbus_interface='com.kylin.systemupgrade.interface')
        return self.update_interface
    '''
    def set_max_inhibit_time(self,time):
        login_proxy = self.get_logind_proxy()
        #首先设置systemd默认延长时间为1800
        try:
            getter_interface = dbus.Interface(
                        login_proxy,
                        dbus_interface='org.freedesktop.login1.Manager')
            ret = getter_interface.SetInhibitDelayMaxSec(time)
        except Exception as e:
            logging.error(e)
    '''

    def get_logind_proxy(self):
        """ Get logind dbus proxy object """
        if not self.logind_proxy:
            bus = dbus.SystemBus()
            if self.inhibit_lock is None:
                # try to get inhibit_lock or throw exception quickly when
                # logind is down
                self.inhibit_lock = self.get_inhibit_shutdown_lock()
            self.logind_proxy = bus.get_object(
                'org.freedesktop.login1', '/org/freedesktop/login1')
        return self.logind_proxy

    def get_inhibit_shutdown_lock(self):
        """ Take delay inhibitor lock """
        bus = dbus.SystemBus()
        return bus.call_blocking(
            'org.freedesktop.login1', '/org/freedesktop/login1',
            'org.freedesktop.login1.Manager', 'Inhibit', 'ssss',
            ('shutdown', 'Unattended Upgrades Shutdown',
             _('Stop ongoing upgrades or perform upgrades before shutdown'),
             'delay'), timeout=2.0)

    def get_inhibit_max_delay(self):
        try:
            logind_proxy = self.get_logind_proxy()
            getter_interface = dbus.Interface(
                logind_proxy,
                dbus_interface='org.freedesktop.DBus.Properties')
            return (getter_interface.Get(
                "org.freedesktop.login1.Manager", "InhibitDelayMaxUSec")
                    / (1000 * 1000))
        except dbus.exceptions.DBusException:
            return 3

    def is_preparing_for_shutdown(self):
        if not self.shutdown_pending:
            try:
                logind_proxy = self.get_logind_proxy()
                getter_interface = dbus.Interface(
                    logind_proxy,
                    dbus_interface='org.freedesktop.DBus.Properties')
                self.shutdown_pending = getter_interface.Get(
                    "org.freedesktop.login1.Manager", "PreparingForShutdown")
            except dbus.exceptions.DBusException:
                return False
        return self.shutdown_pending

    def start_iterations(self):
        while self.iter():
            time.sleep(1)
        '''
        if not self.iter_timer_set:
            try:
                GLib.timeout_add(self.wait_period * 1000, self.iter)
                # schedule first iteration immediately
                GLib.timeout_add(0, lambda: self.iter() and False)
            except NameError:
                pass
        '''
        return True
    '''
    def run_polling(self, signal_handler):
        logging.warning(
            _("Unable to monitor PrepareForShutdown() signal, polling "
              "instead."))
        logging.warning(
            _("To enable monitoring the PrepareForShutdown() signal "
              "instead of polling please install the python3-gi package"))

        signal.signal(signal.SIGTERM, signal_handler)
        signal.signal(signal.SIGHUP, signal_handler)

        # poll for PrepareForShutdown then run final iterations
        if self.options.wait_for_signal:
            logging.debug("Waiting for signal to start operation ")
            while (not self.stop_signal_received.is_set()
                   and not self.is_preparing_for_shutdown()):
                self.stop_signal_received.wait(self.wait_period)
        else:
            logging.debug("Skip waiting for signals, starting operation "
                          "now")
        while not self.iter():
            # TODO iter on sigterm and sighup, too
            time.sleep(self.wait_period)
    '''
    # 定时下载 执行函数
    def timing_download(self):
        env = copy.copy(os.environ)
        logging.debug("starting unattended-upgrades in timing download mode")
        timing_download_ret = subprocess.run(["kylin-unattended-upgrade","--download-only"], env=env)
        if timing_download_ret.returncode == 0:
            logging.debug("kylin-unattended-upgrade download success.")
        else:
            logging.debug("kylin-unattended-upgrade download %d .",timing_download_ret.returncode)

    # 定时安装 执行函数
    def timing_install(self):
        env = copy.copy(os.environ)
        logging.debug("starting unattended-upgrades in timing install mode")
        timing_install_ret = subprocess.run(["kylin-unattended-upgrade","--install-only","--mode=timing"], env=env)
        if timing_install_ret.returncode == 0:
            logging.debug("kylin-unattended-upgrade install success.")
        else:
            logging.debug("kylin-unattended-upgrade install %d .",timing_install_ret.returncode)


    def _wait_for_unattended_upgrade_finish(self):
        max_wait_time = 300
        wait_time = 0
	    #read unattended-upgrade status
        status = ReadValueFromFile(UNATTENDED_UPGRADE_CONFIG_FILE_PATH,"UNATTENDED_UPGRADE","autoupdate_run_status")
        while (status != "idle"):
            ReadValueFromFile(UNATTENDED_UPGRADE_CONFIG_FILE_PATH,"UNATTENDED_UPGRADE","autoupdate_run_status")
            time.sleep(1)
            wait_time += 1
            if wait_time >max_wait_time:
                logging.info("wait for uu time out")
                return
        return 0
	
    def _pause_timer(self):
        if self.download_job is not None:
            self.download_job.pause()

        if self.install_job is not None:
            self.install_job.pause()

    def _resume_timer(self):
        if self.download_job is not None:
            self.download_job.resume()

        if self.install_job is not None:
            self.install_job.resume()

    def run(self):
        """ delay shutdown and wait for PrepareForShutdown or other signals""" 
        # if os_release_info['PROJECT_CODENAME'] == 'V10SP1-edu' and os_release_info['SUB_PROJECT_CODENAME']=='mavis':
        #     pass
        # elif time.time() - float(time_stamp) < float(upgrade_interval):
        #     time_str1 = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(float(time_stamp)))    
        #     time_str2 = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
        #     logging.info("upgrade interval not satisfied:%s-%s"%(time_str1,time_str2))
        #     return 0 

        # set signal handlers
        '''
        def signal_handler(signum, frame):
            
            logging.warning(
                "SIGTERM or SIGHUP received, stopping unattended-upgrades "
                "only if it is running")
            self.stop_signal_received.set()
            #self.start_iterations()
        
        # fall back to polling without GLib
        try:
            hasattr(GLib, "MainLoop")
        except NameError:
            logging.error("MainLoop Not Found")
            #self.run_polling(signal_handler)
            return
        
        for sig in (signal.SIGTERM, signal.SIGHUP):
            GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, sig,
                                 signal_handler, None, None)
        '''
        if self.options.wait_for_signal:
            def change_upgrade_policy_handler():
                if os.path.exists(UNATTENDED_UPGRADE_POLICY_FILE_PATH):
                    autoupgradepolicy.reload_config()

                    if autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on':
                        random_time = get_random_time(autoupgradepolicy.GetOptionValue('downloadTime'),DOWNLOAD_RANDOM)
                        if autoupgradepolicy.GetOptionValue('downloadMode') == 'timing':
                            background_scheduler.add_job(task,'cron', args=['download'],id='download', \
                                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
                        if autoupgradepolicy.GetOptionValue('installMode') == 'timing':
                            random_time = random_time + datetime.timedelta(minutes=INTERVAL_DOWN_INSTALL)
                            background_scheduler.add_job(task,'cron', args=['install'],id='install', \
                                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
                    else:
                        background_scheduler.pause_job('download')
                        background_scheduler.pause_job('install')

                    if autoupgradepolicy.GetOptionValue('preDownload') == 'on':
                        random_time = get_random_time(autoupgradepolicy.GetOptionValue('preDownloadTime'),PREDOWNLOAD_RANDOM)
                        background_scheduler.add_job(task,'cron', args=['predownload'],id='download', \
                                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
                    else:
                        background_scheduler.pause_job('predownload')

                    joblist = background_scheduler.get_jobs()

                    for job in joblist:    
                        logging.debug("job:%s,next run time:%s"%(job.id,job.next_run_time))
                    '''
                    self.download_mode = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_MODE)
                    self.install_mode = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_INSTALL_MODE)
                    self.preDownload = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_PREDOWNLOAD)
                    self.autoUpgrade = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_AUTOUPGRADE)
                    self.download_random = int(kylin_system_updater.GetConfigValue('AutoUpgradeConfig','downloadRandom')[1])
                    self.upgrade_interval = int(kylin_system_updater.GetConfigValue('AutoUpgradeConfig','upgradeInterval')[1])
                    download_time = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_TIME)
                    timelist = download_time.strip().split(':')
                    if len(timelist) != TimeElement.TIME_NUM:
                        logging.debug("unattended-upgrades-policy.conf time err %s",download_time)
                        return
                    # 检查 传入时间 安全性
                    try:
                        tmphour = int(timelist[TimeElement.TIME_HOUR])
                    except ValueError:
                        logging.debug("unattended-upgrades-policy.conf download_time h error")
                        return
                    try:
                        tmpminute = int(timelist[TimeElement.TIME_MINUTE])
                    except ValueError:
                        logging.debug("unattended-upgrades-policy.conf download_time m error")
                        return
        
                    self.download_time['h'] = tmphour
                    self.download_time['m'] = tmpminute
                    self.download_time_r = convert_time_by_random(self.download_time, self.download_random)
                    self.install_time['h']  = self.download_time_r['h']
                    self.install_time['m']  = self.download_time_r['m'] + INTERVAL_DOWN_INSTALL
                    self.install_time_r = convert_time_by_random(self.install_time, INSTALL_RANDOM)
                    
                    logging.info("download random:%d,upgrade interval:%d"%(self.download_random,self.upgrade_interval))
                    
                    if self.preDownload == FeatureSwitch.ON.value: #open download timing
                        download_time_tmp = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_DOWNLOAD_TIME)
                        timelist = download_time_tmp.split(':')
                        if len(timelist) != TimeElement.TIME_NUM:
                            logging.debug("unattended-upgrades-policy.conf time err %s",download_time_tmp)
                            return
                        # 检查 传入时间 安全性
                        try:
                            tmphour = int(timelist[TimeElement.TIME_HOUR])
                        except ValueError:
                            logging.debug("unattended-upgrades-policy.conf download_time h error")
                        else:
                            self.download_time['h'] = tmphour
                        try:
                            tmpminute = int(timelist[TimeElement.TIME_MINUTE])
                        except ValueError:
                            logging.debug("unattended-upgrades-policy.conf download_time m error")
                        else:
                            self.download_time['m'] = tmpminute
                        self.download_time_r = convert_time_by_random(self.download_time, self.download_random)
                        try:
                            if self.download_job is not None:
                                self.download_job.remove()
                        except Exception as e:
                            logging.error(e)
                        logging.info("pre-download time:%d:%d"%(self.download_time_r['h'], self.download_time_r['m']))
                        self.download_job = self.scheduler.add_job(self.timing_download, 'cron', hour=self.download_time_r['h'], minute=self.download_time_r['m'])
                    elif self.preDownload == FeatureSwitch.OFF.value:
                       pass 
                    
                    else: #close download timing
                        try:
                            self.download_job.pause()
                        except Exception as e:
                            logging.error(e)
                    
                    
                    if self.autoUpgrade == FeatureSwitch.OFF.value:
                        logging.info("auto upgrade turned off,removing download and instal jobs...")
                        try:
                            if self.download_job.pending:
                                self.download_job.remove()
                        except Exception as e:
                            pass
                            # logging.error(e)
                        try:
                            if self.install_job.pending:
                                self.install_job.remove()
                        except Exception as e:
                            pass
                            # logging.error(e)
                        return 
                    else:
                        if self.download_mode == DownloadMode.TIMING_DOWNLOAD.value:
                            try:
                                if self.download_job.pending:
                                    self.download_job.remove()
                            except Exception as e:
                                pass
                                # logging.error(e) 
                            logging.info("download time:%d:%d"%(self.download_time_r['h'], self.download_time_r['m']))   
                            self.download_job = self.scheduler.add_job(self.timing_download, 'cron', hour=self.download_time_r['h'], minute=self.download_time_r['m'])        
                        else:
                            try:
                                if self.download_job.pending:
                                    self.download_job.remove()
                            except Exception as e:
                                pass
                                # logging.error(e)

                        if self.install_mode == InstallMode.TIMING_INSTALL.value:                        
                            try:
                                if self.install_job.pending:
                                    self.install_job.remove()
                            except Exception as e:
                                pass
                                # logging.error(e)
                            logging.info("install time:%d:%d"%(self.install_time_r['h'], self.install_time_r['m']))
                            self.install_job = self.scheduler.add_job(self.timing_install, 'cron', hour=self.install_time_r['h'], minute=self.install_time_r['m'])
                        elif self.install_mode == InstallMode.BEFORE_SHUTDOWN_INSTALL.value:
                            try:
                                if self.install_job.pending:
                                    self.install_job.remove()
                            except Exception as e:
                                pass
                                # logging.error(e)
                            logging.debug("install job removed,installation will conduct before shutdown")
                        else: #close install timing
                            try:
                                if self.download_job.pending:
                                    self.install_job.remove()
                            except Exception as e:
                                pass
                                # logging.error(e)
                    
                    
                    logging.info("upgrade time: [%d:%d] [%d:%d] predown[%s] autoupgrade[%s] d-mode[%s] i-mode[%s]", 
                        self.download_time_r['h'], self.download_time_r['m'],self.install_time_r['h'],self.install_time_r['m'],
                        self.preDownload, self.autoUpgrade, self.download_mode, self.install_mode)
                    '''
                else:
                    logging.debug("unattended-upgrades-policy.conf not exist")

            def upgrade_all_now_handler():
                now=datetime.datetime.now()
                random_time = now + datetime.timedelta(minutes=DOWNLOAD_RANDOM)
                background_scheduler.add_job(task,'date', args=['download_and_install'],id='download', \
                                hour = random_time.hour,minute = random_time.minute,replace_existing=True)
                
                joblist = background_scheduler.get_jobs()
                
                for job in joblist:    
                    logging.debug("job:%s,next run time:%s"%(job.id,job.next_run_time))
                 #self._wait_for_unattended_upgrade_finish()
                '''
                 if FindRuningUnattendedUpgrades():
                     logging.warning("find runing unattended-upgrades,please wait")
                     return False
                 else:
                     self._pause_timer()
                     env = copy.copy(os.environ)
                     retdownload = subprocess.run(["kylin-unattended-upgrade","--download-only"], env=env)
                     retinstall = subprocess.run(["kylin-unattended-upgrade","--install-only"], env=env)
                     self._resume_timer()
                     if retdownload == 0 and retinstall == 0:
                         return True
                     else:
                         return False
                '''

            def prepare_for_shutdown_handler(active):
                """ Handle PrepareForShutdown() """
                if not active:
                    logging.warning("PrepareForShutdown(false) received, "
                                    "this should not happen")
                # PrepareForShutdown arrived, starting final iterations
                self.install_mode = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH,POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY,AUTO_UPGRADE_POLICY_OPTION_INSTALL_MODE)
                autoUpgrade = ReadValueFromFile(UNATTENDED_UPGRADE_POLICY_FILE_PATH, POLICY_CONF_SECTION_AUTO_UPGRADE_POLICY, AUTO_UPGRADE_POLICY_OPTION_AUTOUPGRADE)
                if os_release_info['PROJECT_CODENAME'] == 'V10SP1-edu' and os_release_info['SUB_PROJECT_CODENAME']=='mavis':
                    bat = get_mavis_capacity()
                    logging.info("battery capacity:%d"%bat)
                    if os.path.exists(PKGS_TO_INSTALL_FLAG_FILE) and bat >=25:
                        logging.info("mavis shutdown install")
                        do_plymouth_splash()
                        self.start_iterations()
                        logging.info("finished iteration")                       
                elif autoUpgrade == FeatureSwitch.ON.value and self.install_mode == InstallMode.BEFORE_SHUTDOWN_INSTALL.value:
                    if self.update_interface.GetConfigValue('AutoUpgradeConfig','shutdown_install'):
                        #show plymouth splash if bsshutdown is set
                        if os.path.exists(PKGS_TO_INSTALL_FLAG_FILE):
                            do_plymouth_splash()
                            self.start_iterations()
                            logging.info("finished iteration")
                else:
                    pass
                self.mainloop.quit()
            self.get_update_proxy()
            self.get_update_interface()
            self.update_proxy.connect_to_signal("ChangeUpgradePolicy",change_upgrade_policy_handler)   
            self.update_proxy.connect_to_signal("UpgradeAllNow",upgrade_all_now_handler)
            
            try:
                self.get_logind_proxy().connect_to_signal(
                    "PrepareForShutdown", prepare_for_shutdown_handler)
            except dbus.exceptions.DBusException:
                logging.warning(
                    _("Unable to monitor PrepareForShutdown() signal, polling "
                      "instead."))
                logging.warning(
                    _("Maybe systemd-logind service is not running."))
                # self.run_polling(signal_handler)
                return
            #self.set_max_inhibit_time(1800)
            logging.debug("Waiting for signal to start operation ")
        else:
            # starting final iterations immediately
            logging.debug("Skip waiting for signals, starting operation "
                          "now")
            # self.start_iterations()
        '''
        if os_release_info['PROJECT_CODENAME'] == 'V10SP1-edu' and os_release_info['SUB_PROJECT_CODENAME']=='mavis':
            logging.info("setting startup download timer")
            GLib.timeout_add(300*1000, lambda: self.timing_download() and False)
            #local_time =time.localtime(time.time()+300)
            self.startup_download_job = self.scheduler.add_job(self.timing_download,'cron',hour=self.download_time_r['h'],minute = self.download_time_r['m'])
        
        else:
            if self.autoUpgrade == FeatureSwitch.ON.value:
                logging.debug("download time:[%d:%d] install time:[%d:%d]", self.download_time_r['h'], self.download_time_r['m'],self.install_time_r['h'],self.install_time_r['m'])
                self.download_job = self.scheduler.add_job(self.timing_download, 'cron', hour=self.download_time_r['h'], minute=self.download_time_r['m'])
                self.install_job = self.scheduler.add_job(self.timing_install, 'cron', hour=self.install_time_r['h'], minute=self.install_time_r['m'])
            elif self.autoUpgrade == FeatureSwitch.OFF.value:
                logging.info("auto upgrade turned off")
        '''
        #TimerThread(self.scheduler).start()
        self.mainloop.run()
        logging.info("quit mainloop")
        os._exit(0)
        #GLib.MainLoop().run()

    def try_iter_on_shutdown(self):
        # check if we need to run unattended-upgrades on shutdown and if
        # so, run it
        try:
            if self.apt_pkg_reinit_done is None:
                logging.debug("Initializing apt_pkg configuration")
                apt_pkg.init_config()
                self.apt_pkg_reinit_done = True
        except apt_pkg.Error as error:
            # apt may be in a transient state due to unattended-upgrades
            # running, thus assuming non shutdown mode is reasonable
            logging.error(_("Apt returned an error thus shutdown mode is "
                            "disabled"))
            logging.error(_("error message: '%s'"), error)
            self.apt_pkg_reinit_done = False

        if self.on_shutdown_mode is None:
            self.on_shutdown_mode = True
                #(
                #not self.options.stop_only
                #and not self.stop_signal_received.is_set()
                #and self.apt_pkg_reinit_done
                # and apt_pkg.config.find_b(
                #     "Unattended-Upgrade::InstallOnShutdown", False)
                #)
            if self.on_shutdown_mode:
                env = copy.copy(os.environ)
                #env["UNATTENDED_UPGRADES_FORCE_INSTALL_ON_SHUTDOWN"] = "1"
                logging.info("starting unattended-upgrades in shutdown mode")
                if FindRuningUnattendedUpgrades():
                    logging.warning("another unattended-upgrade is running , quit")
                    return False
                self.on_shutdown_mode_uu_proc = subprocess.Popen(
                    ["kylin-unattended-upgrade","--install-only","--mode=shutdown"], env=env)
                #log_msg(_("Running unattended-upgrades in shutdown mode"))
                # run u-u, but switch to stopping when receiving stop signal
                # because it means shutdown progressed despite holding the lock

        if self.on_shutdown_mode:
            log_progress()
            if self.on_shutdown_mode_uu_proc.poll() is not None:
                # unattended-upgrades stopped on its own
                #exit_log_result(True)
                if os.path.exists(PKGS_TO_INSTALL_FLAG_FILE):
                    os.remove(PKGS_TO_INSTALL_FLAG_FILE)
                subprocess.call(["/bin/plymouth","system-update","--progress=100"])
                time.sleep(1)
                subprocess.run(["/bin/plymouth","quit","--retain-splash"])
                return False    
            else:
                return True
        return False
        

    def iter(self):
        '''
        if self.start_time is None:
            self.start_time = time.time()
            logging.debug("Starting countdown of %s minutes",
                          self.max_delay / 60)
        else:
            if (time.time() - self.start_time) > self.max_delay:
                logging.warning(_(
                    "Giving up on lockfile after %s minutes of delay"),
                                self.max_delay / 60)
                #os._exit(1)
                #sys.exit(1)
                return False
        
        if not self.stop_signal_received.is_set():
            if self.try_iter_on_shutdown():
                return True
            else:
                return False
        
        
        # run monitoring and keep "UI" updated
        res = apt_pkg.get_lock(self.options.lock_file)
        logging.debug("get_lock returned %i" % res)
        # exit here if there is no lock
        if res > 0:
            logging.debug("lock not taken")
            #os._exit(0)
            if self.lock_was_taken:
                exit_log_result(self.signal_sent)
            else:
                sys.exit(0)
        self.lock_was_taken = True
        signal_stop_unattended_upgrade()
        self.signal_sent = True
        # show log
        log_progress()
        '''
        return self.try_iter_on_shutdown()
        #return True
        

def main():
    # setup gettext
    localesApp = "unattended-upgrades"
    localesDir = "/usr/share/locale"
    gettext.bindtextdomain(localesApp, localesDir)
    gettext.textdomain(localesApp)

    # use a normal logfile instead of syslog too as on shutdown its too
    # easy to get syslog killed
    logdir = "/var/log/kylin-unattended-upgrades/"
    try:
        apt_pkg.init_config()
        # logdir = apt_pkg.config.find_dir(
        #     "Unattended-Upgrade::LogDir", logdir)
    except apt_pkg.Error as error:
        logging.error(_("Apt returned an error when loading configuration, "
                        "using default values"))
        logging.error(_("error message: '%s'"), error)

    parser = OptionParser()
    parser.add_option("", "--debug",
                      action="store_true", dest="debug",
                      default=apt_pkg.config.find_b(
                          "Unattended-Upgrade::Debug", True),
                      help="print debug messages")
    parser.add_option("", "--delay", default=25, type="int",
                      help="delay in minutes to wait for unattended-upgrades")
    parser.add_option("", "--lock-file",
                      default="/var/run/kylin-unattended-upgrades.lock",
                      help="lock file location")
    parser.add_option("", "--stop-only",
                      action="store_true", dest="stop_only", default=False,
                      help="only stop running unattended-upgrades, don't "
                      "start it even when "
                      "Unattended-Upgrade::InstallOnShutdown is true")
    parser.add_option("", "--wait-for-signal",
                      action="store_true", dest="wait_for_signal",
                      default=False,
                      help="wait for TERM signal before starting operation")
    (options, args) = parser.parse_args()

    # setup logging
    level = logging.INFO
    if options.debug:
        level = logging.DEBUG
    if not os.path.exists('/var/lib/unattended-upgrades'):
        os.makedirs('/var/lib/unattended-upgrades')
    if not os.path.exists(logdir):
        os.makedirs(logdir)
    logfile = os.path.join(logdir, "unattended-upgrades-shutdown.log")
    logging.basicConfig(filename=logfile,
                        level=level,
                        format="%(asctime)s %(levelname)s - %(message)s")
    clean_flag_files(flag_file_list)
    signal.signal(signal.SIGTERM, signal_term_handler)
    signal.signal(signal.SIGHUP, signal.SIG_IGN)
    # init()
    
    UnattendedUpgradesShutdown(options).run()


if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option("", "--debug",
                      action="store_true", dest="debug",
                      default=True,#apt_pkg.config.find_b(
                          #"Unattended-Upgrade::Debug", True),
                      help="print debug messages")
    parser.add_option("", "--delay", default=25, type="int",
                      help="delay in minutes to wait for unattended-upgrades")
    parser.add_option("", "--lock-file",
                      default="/var/run/kylin-unattended-upgrades.lock",
                      help="lock file location")
    parser.add_option("", "--stop-only",
                      action="store_true", dest="stop_only", default=False,
                      help="only stop running unattended-upgrades, don't "
                      "start it even when "
                      "Unattended-Upgrade::InstallOnShutdown is true")
    parser.add_option("", "--wait-for-signal",
                      action="store_true", dest="wait_for_signal",
                      default=False,
                      help="wait for TERM signal before starting operation")
    (options, args) = parser.parse_args()
    logdir = "/var/log/kylin-unattended-upgrades/"
    # setup logging
    level = logging.INFO
    if options.debug:
        level = logging.DEBUG
    if not os.path.exists('/var/lib/unattended-upgrades'):
        os.makedirs('/var/lib/unattended-upgrades')
    if not os.path.exists(logdir):
        os.makedirs(logdir)
    logfile = os.path.join(logdir, "unattended-upgrades-shutdown.log")
    logging.basicConfig(filename=logfile,
                        level=level,
                        format="%(asctime)s %(levelname)s - %(message)s")
    logging.getLogger('apscheduler').setLevel(logging.DEBUG)
    os_release_info = ReadOsRelease('/etc/os-release')
    logging.info("project id:%s,sub-project id:%s"%(os_release_info['PROJECT_CODENAME'],os_release_info['SUB_PROJECT_CODENAME']))
    
    time_stamp = "0"
    if os.path.exists(TIME_STAMP):
        with open(TIME_STAMP,'r') as f:
            time_stamp = f.readline()
            logging.info("time stamp:%s"%time_stamp)
    # setup gettext
    localesApp = "unattended-upgrades"
    localesDir = "/usr/share/locale"
    gettext.bindtextdomain(localesApp, localesDir)
    gettext.textdomain(localesApp)

    # use a normal logfile instead of syslog too as on shutdown its too
    # easy to get syslog killed
    
    try:
        # apt_pkg.init_config()
        reload_options_config()
        # logdir = apt_pkg.config.find_dir(
        #     "Unattended-Upgrade::LogDir", logdir)
    except apt_pkg.Error as error:
        logging.error(_("Apt returned an error when loading configuration, "
                        "using default values"))
        logging.error(_("error message: '%s'"), error)

    
    clean_flag_files(flag_file_list)
    signal.signal(signal.SIGTERM, signal_term_handler)
    signal.signal(signal.SIGHUP, signal.SIG_IGN)
    dpkg_fix=None
    if os_release_info['PROJECT_CODENAME'] == 'V10SP1-edu' and os_release_info['SUB_PROJECT_CODENAME']=='mavis':
        dpkg_journal_dirty = is_dpkg_journal_dirty()
        logging.info("dpkg dirty:%s"%(dpkg_journal_dirty))
        if dpkg_journal_dirty:
            try:
                with open(OTA_PKGS_TO_INSTALL_LIST,'r') as f:
                    pkgs = f.read()
                    ret = subprocess.run(["dpkg -i %s"%pkgs],shell=True,stdout=open(logfile,'a+'),stderr=open(logfile,'a+'))
            except Exception as e:
                logging.error(e)
            if ret.returncode == 0:
                logging.info("dpkg fix success")
                subprocess.Popen('dbus-send --system --type=signal / com.kylin.update.notification.FixFinish', shell=True)
            # dpkg_fix = subprocess.run("dpkg --configure -a",shell=True,stdout=open(logfile,'a+'),stderr=open(logfile,'a+'))
        abnormal_pkg_count = get_abnormally_installed_pkg_count()
        logging.info("abnormal pkg count:%s"%(abnormal_pkg_count))
        if abnormal_pkg_count != '0':
            apt_fix = subprocess.run("echo y|apt install -f",shell=True,stdout=open(logfile,'a+'),stderr=open(logfile,'a+'))
    kylin_system_updater = KylinSystemUpdater()
    autoupgradepolicy = AutoUpgradePolicy()
    background_scheduler = BackgroundScheduler(timezone = "Asia/Shanghai")
    background_scheduler_init(background_scheduler)
    UnattendedUpgradesShutdown(options).run()
    #main()
